package com.inifitness.tenant.core.security.util;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.inifitness.common.constants.GlobalConstants;
import com.inifitness.tenant.core.security.constant.JwtClaimConstants;
import com.inifitness.tenant.core.security.model.BizUserAcctDetails;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.RegisteredPayload;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

/**
 * JWT 工具类
 *
 * @author sunjinfeng
 * @since 2024/04/18 11:00:00
 */
@Component
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JwtUtils {

  /**
   * JWT 加解密使用的密钥
   */
  private static byte[] key;


  /**
   * JWT Token 的有效时间(单位:秒)
   */
  private static int ttl;


  @Value("${jwt.key}")
  public static void setKey(String key) {
    JwtUtils.key = key.getBytes();
  }

  @Value("${jwt.ttl}")
  public static void setTtl(Integer ttl) {
    JwtUtils.ttl = ttl;
  }

  /**
   * 生成 JWT Token
   *
   * @param authentication 用户认证信息
   * @return Token 字符串
   */
  public static String generateToken(Authentication authentication) {

    BizUserAcctDetails userDetails = (BizUserAcctDetails) authentication.getPrincipal();

    Map<String, Object> payload = new HashMap<>();
    payload.put(JwtClaimConstants.USER_ID, userDetails.getUserId());
    payload.put(JwtClaimConstants.TENANT_ID, userDetails.getDeptId());
    payload.put(JwtClaimConstants.DATA_SCOPE, userDetails.getDataScope());

    // claims 中添加角色信息
    Set<String> roles = userDetails.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toSet());
    payload.put(JwtClaimConstants.AUTHORITIES, roles);

    Date now = new Date();
    Date expiration = DateUtil.offsetSecond(now, ttl);
    payload.put(RegisteredPayload.ISSUED_AT, now);
    payload.put(RegisteredPayload.EXPIRES_AT, expiration);
    payload.put(RegisteredPayload.SUBJECT, authentication.getName());
    payload.put(RegisteredPayload.JWT_ID, IdUtil.simpleUUID());

    return JWTUtil.createToken(payload, key);
  }


  /**
   * 从 JWT Token 中解析 Authentication  用户认证信息
   *
   * @param payload JWT 载体
   * @return 用户认证信息
   */
  public static UsernamePasswordAuthenticationToken getAuthentication(Map<String, Object> payload) {
    BizUserAcctDetails userDetails = new BizUserAcctDetails();
    userDetails.setUserId(Convert.toLong(payload.get(JwtClaimConstants.USER_ID)));
    userDetails.setDeptId(Convert.toLong(payload.get(JwtClaimConstants.TENANT_ID)));
    userDetails.setDataScope(Convert.toInt(payload.get(JwtClaimConstants.DATA_SCOPE)));

    userDetails.setUsername(Convert.toStr(payload.get(RegisteredPayload.SUBJECT)));
    // 角色集合
    Set<SimpleGrantedAuthority> authorities = ((JSONArray) payload.get(
        JwtClaimConstants.AUTHORITIES))
        .stream()
        .map(authority -> new SimpleGrantedAuthority(Convert.toStr(authority)))
        .collect(Collectors.toSet());

    return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
  }


  /**
   * 解析 JWT Token 获取载体信息
   *
   * @param token JWT Token
   * @return 载体信息
   */
  public static Map<String, Object> parseToken(String token) {
    try {
      if (CharSequenceUtil.isBlank(token)) {
        return Collections.emptyMap();
      }

      String tokenValue = token;
      if (token.startsWith(GlobalConstants.BEARER_TOKEN_TYPE)) {
        tokenValue = token.substring(GlobalConstants.BEARER_TOKEN_LENGTH);
      }

      JWT jwt = JWTUtil.parseToken(tokenValue);
      if (jwt.setKey(key).validate(0)) {
        return jwt.getPayloads();
      }
    } catch (Exception ignored) {
    }
    return Collections.emptyMap();
  }
}
